// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

import { Button, ComboBox, Palette } from "std-widgets.slint";
import { CustomLineEdit } from "../widget-helpers.slint";
import { ColorIndicator, FakeShadowText, PickerTextInput } from "../brush-helpers.slint";
import { SimpleColumn } from "../../../components/layout-helpers.slint";
import { GradientType, WidgetMode, WindowManager, PickerData } from "../../../windowglobal.slint";
import { Api, BrushKind, GradientStop } from "../../../api.slint";
import { Icons, EditorPalette, EditorSizeSettings, PickerStyles } from "../../../components/styling.slint";

struct GradientStopIndexChanged {
    start-index: int,
    end-index: int,
    initiated-by-index: int,
}

component GradientSlider {
    in property <length> start-limit;
    in property <length> end-limit;
    in property <length> parent-width;
    in property <bool> active: stop-index == PickerData.current-stop-index;
    property <bool> being-dragged <=> ta.pressed;
    property <length> slider-target-x;
    in-out property <int> stop-index;
    out property pressed <=> ta.pressed;

    private property <int> previous-stop-index;
    in property <GradientStopIndexChanged> index-changed-data;

    callback index-changed(change: GradientStopIndexChanged);

    changed index-changed-data => {
        // is the new end index match? If so swap with the start-index
        if !self.being-dragged {
            if self.index-changed-data.end-index == self.stop-index {
                self.stop-index = self.index-changed-data.start-index;
            }
        }
    }

    width: 0;
    height: 0;
    x: PickerData.current-gradient-stops[stop-index].position * parent-width;

    Rectangle {
        width: 24px;
        height: self.width;
        background: active ? PickerStyles.focused-color : PickerStyles.slider-color;
        border-radius: 5px;

        property <bool> block-clicked: false;
        ta := TouchArea {
            moved => {
                slider-target-x = ((root.x + self.mouse-x - self.pressed-x) / 1px).round() * 1px;
                if root.x != slider-target-x {
                    block-clicked = true;
                }
                root.previous-stop-index = root.stop-index;
                root.stop-index = Api.move-gradient-stop(PickerData.current-gradient-stops, stop-index, clamp(slider-target-x, root.start-limit, root.end-limit) / root.parent-width);

                if root.previous-stop-index != root.stop-index {
                    root.index-changed({ start-index: root.previous-stop-index, end-index: root.stop-index });
                    PickerData.current-stop-index = root.stop-index;
                    previous-stop-index = root.stop-index;
                }
            }
            clicked => {
                if !block-clicked {
                    WindowManager.show-color-stop-picker();
                }
                block-clicked = false;
            }
            changed pressed => {
                if self.pressed {
                    PickerData.current-stop-index = stop-index;
                }
            }
        }

        Rectangle {
            x: parent.width / 2;
            y: 1px;
            width: 0;
            height: 0;
            Image {
                width: 10px;
                transform-rotation: 45deg;
                source: Icons.black-square;
                colorize: active ? PickerStyles.focused-color : PickerStyles.slider-color;
            }
        }

        ColorIndicator {
            color: PickerData.current-gradient-stops[stop-index].color;
        }
    }
}

component GradientStopValue {
    in property <bool> focused: false;
    in-out property <int> stop-index;
    property <float> stop-data-position: PickerData.current-gradient-stops[stop-index].position;
    property <color> stop-data-color: PickerData.current-gradient-stops[stop-index].color;
    property <bool> active: stop-index == PickerData.current-stop-index;

    changed stop-data-position => {
        ti-position.text = (stop-data-position * 100.0).round();
    }
    changed stop-data-color => {
        ti-alpha.text = (stop-data-color.to-hsv().alpha * 100.0).round();
        ti-stop-color.text = Api.color-to-data(stop-data-color).short-text.to-uppercase();
    }
    width: 100%;
    height: 32px;

    TouchArea {
        changed pressed => {
            if self.pressed {
                PickerData.current-stop-index = stop-index;
            }
        }
    }

    Rectangle {
        background: active ? PickerStyles.focused-stop-color : transparent;
    }

    Rectangle {
        height: 24px;

        Rectangle {
            x: EditorSizeSettings.standard-margin;
            width: 48px;
            border-radius: EditorSizeSettings.property-border-radius;
            background: EditorPalette.section-color;
            ti-position := PickerTextInput {
                x: -20px;
                width: 50px;
                text: (PickerData.current-gradient-stops[stop-index].position * 100.0).round();
                horizontal-alignment: right;
                changed has-focus => {
                    if !self.has-focus {
                        ti-position.text = (stop-data-position * 100.0).round();
                    } else {
                        PickerData.current-stop-index = stop-index;
                    }
                }
                accepted => {
                    PickerData.current-stop-index = Api.move-gradient-stop(PickerData.current-gradient-stops, stop-index, clamp(self.text.to-float(), 0, 100) / 100);
                    self.clear-focus();
                }
                edited => {
                    if self.text.character-count > 3 {
                        self.text = clamp(self.text.to-float(), 0, 100);
                    }
                }
            }

            percent-label := FakeShadowText {
                x: parent.width - self.width - 5px;
                font-family: "Inter";
                font-size: 11px;
                text: "%";
            }
        }

        Rectangle {
            x: 70px;
            width: 140px;
            border-radius: EditorSizeSettings.property-border-radius;
            background: EditorPalette.section-color;

            ColorIndicator {
                x: (parent.height - 15px) / 2;
                color: PickerData.current-gradient-stops[stop-index].color;
                TouchArea {
                    changed pressed => {
                        if self.pressed {
                            PickerData.current-stop-index = stop-index;
                        }
                    }
                    clicked => {
                        WindowManager.show-color-stop-picker();
                    }
                }
            }

            FakeShadowText {
                x: 25px;
                font-family: "Inter";
                font-size: 11px;
                text: "#";
            }

            ti-stop-color := PickerTextInput {
                x: 35px;
                text: Api.color-to-data(PickerData.current-gradient-stops[stop-index].color).short-text.to-uppercase();
                letter-spacing: 0.8px;
                input-type: text;

                property <{hue: float, saturation: float, value: float}> hsv-color;
                function apply-text(text: string) {
                    if Api.string-is-color("#\{self.text}") {
                        PickerData.hue = Api.string-to-color("#\{self.text}").to-hsv().hue;
                        PickerData.saturation = Api.string-to-color("#\{self.text}").to-hsv().saturation;
                        PickerData.value = Api.string-to-color("#\{self.text}").to-hsv().value;
                        if self.text.character-count > 6 {
                            PickerData.current-gradient-stops[stop-index].color = Api.string-to-color("#\{self.text}");
                        } else {
                            hsv-color = Api.string-to-color("#\{self.text}").to-hsv();
                            PickerData.current-gradient-stops[stop-index].color = hsv(hsv-color.hue, hsv-color.saturation, hsv-color.value, PickerData.current-gradient-stops[stop-index].color.to-hsv().alpha);
                        }
                    } else {
                        self.text = Api.color-to-data(PickerData.current-color).short-text.to-uppercase();
                    }
                }

                accepted => {
                    apply-text(self.text);
                    self.clear-focus();
                }
                edited => {
                    if self.text.character-count > 8 {
                        self.text = Api.color-to-data(PickerData.current-color).short-text.to-uppercase();
                    }
                }
                changed has-focus => {
                    if self.has-focus {
                        PickerData.current-stop-index = stop-index;
                    } else {
                        apply-text(self.text);
                    }
                }
            }

            divider := Rectangle {
                x: parent.width - 45px;
                width: 1px;
                height: parent.height;
                background: Palette.background;
            }

            Rectangle {
                width: 48px;
                x: parent.width - self.width;
                ti-alpha := PickerTextInput {
                    x: -20px;
                    horizontal-alignment: right;
                    text: (PickerData.current-gradient-stops[stop-index].color.to-hsv().alpha * 100.0).round();
                    accepted => {
                        PickerData.current-gradient-stops[stop-index].color = hsv(PickerData.current-gradient-stops[stop-index].color.to-hsv().hue, PickerData.current-gradient-stops[stop-index].color.to-hsv().saturation, PickerData.current-gradient-stops[stop-index].color.to-hsv().value, clamp(self.text.to-float(), 0, 100) / 100);
                        self.clear-focus();
                    }
                    edited => {
                        if self.text.character-count > 3 {
                            self.text = clamp(self.text.to-float(), 0, 100).round();
                        }
                    }
                    changed has-focus => {
                        if self.has-focus {
                            PickerData.current-stop-index = stop-index;
                        }
                    }
                }

                FakeShadowText {
                    x: parent.width - self.width - 5px;
                    font-family: "Inter";
                    font-size: 11px;
                    text: "%";
                }

                Rectangle {
                    x: parent.width - self.width;
                    width: 20px;
                    TouchArea {
                        mouse-cursor: col-resize;
                        property <float> initial-alpha: 0;
                        changed pressed => {
                            if self.pressed {
                                PickerData.current-stop-index = stop-index;
                                initial-alpha = PickerData.current-gradient-stops[stop-index].color.to-hsv().alpha;
                            }
                        }
                        moved => {
                            PickerData.current-gradient-stops[stop-index].color = hsv(PickerData.current-gradient-stops[stop-index].color.to-hsv().hue, PickerData.current-gradient-stops[stop-index].color.to-hsv().saturation, PickerData.current-gradient-stops[stop-index].color.to-hsv().value, (initial-alpha + ((self.mouse-x - self.pressed-x) / 1px) / 100).clamp(0, 1));
                        }
                    }
                }
            }
        }
    }

    Rectangle {
        x: parent.width - self.width - 5px;
        width: 25px;
        height: self.width;
        visible: PickerData.current-gradient-stops.length > 1;
        Rectangle {
            opacity: 20%;
            background: delete-stop-ta.has-hover ? EditorPalette.section-color : transparent;
            border-radius: EditorSizeSettings.property-border-radius;
        }

        delete-stop-ta := TouchArea {
            clicked => {
                Api.remove-gradient-stop(PickerData.current-gradient-stops, stop-index);
                PickerData.rebuild-gradient-stops();
            }
        }

        Rectangle {
            width: 12px;
            height: 1px;
            border-radius: self.height / 2;
            background: EditorPalette.text-color;
        }
    }
}

export component GradientPicker inherits SimpleColumn {
    in property <WidgetMode> widget-mode;
    property <float> current-angle <=> PickerData.current-angle;
    property <GradientStopIndexChanged> index-changed;

    changed current-angle => {
        degrees.text = current-angle.round();
    }

    callback clear-focus-panel();

    Rectangle {
        height: cb.height + (EditorSizeSettings.small-margin * 2);

        cb := ComboBox {
            x: EditorSizeSettings.standard-margin;
            width: 160px;
            model: ["Conic", "Linear", "Radial"];
            current-index: PickerData.current-brush-kind == BrushKind.conic ? 0 : PickerData.current-brush-kind == BrushKind.linear ? 1 : 2;
            selected(value) => {
                PickerData.set-gradient-type(value == "Linear" ? GradientType.linear : value == "Radial" ? GradientType.radial : GradientType.conic );
            }
        }

        degrees := CustomLineEdit {
            x: parent.width - self.width - EditorSizeSettings.standard-margin;
            width: 40px;
            visible: cb.current-index == 1;
            Rectangle {
                x: parent.width - self.width;
                width: 48px;
                height: parent.height;

                pi := PickerTextInput {
                    x: -12px;
                    text: PickerData.current-angle.round();
                    horizontal-alignment: right;
                    accepted => {
                        self.text = self.text.to-float().mod(360);
                        PickerData.current-angle = self.text.to-float();
                        self.clear-focus();
                    }
                    edited => {
                        if self.text.character-count > 3 {
                            self.text = self.text.to-float().mod(360);
                        }
                    }
                    changed has-focus => {
                        if !self.has-focus {
                            self.text = self.text.to-float().mod(360);
                            PickerData.current-angle = self.text.to-float();
                        }
                    }
                }

                FakeShadowText {
                    x: parent.width - self.width - 4px;
                    font-family: "Inter";
                    font-size: 11px;
                    text: "°";
                }

                Rectangle {
                    x: parent.width - self.width;
                    width: 20px;
                    TouchArea {
                        mouse-cursor: col-resize;
                        property <float> initial-angle: 0;
                        changed pressed => {
                            if self.pressed {
                                initial-angle = PickerData.current-angle;
                            }
                        }
                        moved => {
                            PickerData.current-angle = (initial-angle + ((self.mouse-x - self.pressed-x) * 2 / 1px)).mod(360).round();
                        }
                    }
                }
            }
        }
    }

    gradient-visual := Rectangle {
        height: 50px;

        Rectangle {
            y: 0;
            width: parent.width - (EditorSizeSettings.standard-margin * 2);
            height: 30px;

            Rectangle {
                border-radius: EditorSizeSettings.property-border-radius;
                clip: true;
                background: white;

                Image {
                    width: 100%;
                    height: 100%;
                    vertical-alignment: top;
                    horizontal-alignment: left;
                    source: Icons.checkerboard-tiny;
                    vertical-tiling: repeat;
                    horizontal-tiling: repeat;
                    colorize: #e1e1e1;
                }

                Rectangle {
                    background: Api.create-brush(BrushKind.linear, 90, PickerData.current-brush, PickerData.current-gradient-stops);

                    TouchArea {
                        clicked() => {
                            PickerData.current-stop-index = Api.add-gradient-stop(PickerData.current-gradient-stops, Api.suggest-gradient-stop-at-position(PickerData.current-gradient-stops, self.mouse-x / self.width));
                            PickerData.rebuild-gradient-stops();
                        }
                    }
                }

                Rectangle {
                    border-radius: parent.border-radius;
                    border-width: 1px;
                    border-color: EditorPalette.text-color.with-alpha(10%);
                }
            }

            property <GradientStopIndexChanged> index-changed;

            for i[index] in PickerData.current-gradient-stops: GradientSlider {
                y: parent.height;
                start-limit: 0;
                end-limit: parent.width;
                parent-width: parent.width;
                stop-index: index;

                index-changed-data <=> parent.index-changed;

                index-changed(change) => {
                    parent.index-changed = change;
                }

                changed pressed => {
                    if self.pressed {
                        root.clear-focus-panel();
                    }
                }
            }
        }
    }

    Rectangle {
        height: 30px;
        Text {
            x: EditorSizeSettings.standard-margin;
            color: EditorPalette.text-color;
            text: "Stops";
            font-family: "Inter";
            font-size: 11px;
        }

        Rectangle {
            x: parent.width - self.width - 5px;
            width: 25px;
            height: self.width;
            background: t-plus.has-hover ? EditorPalette.section-color : transparent;
            border-radius: EditorSizeSettings.property-border-radius;

            t-plus := TouchArea {
                clicked() => {
                    PickerData.current-stop-index = Api.add-gradient-stop(PickerData.current-gradient-stops, Api.suggest-gradient-stop-at-row(PickerData.current-gradient-stops, PickerData.current-stop-index + 1));
                    PickerData.rebuild-gradient-stops();
                }
            }

            Image {
                source: Icons.plus;
                colorize: EditorPalette.text-color;
            }
        }
    }

    SimpleColumn {
        for i[index] in PickerData.current-gradient-stops: GradientStopValue {
            stop-index: index;
        }
    }

    if widget-mode == WidgetMode.edit: Rectangle {
        height: EditorSizeSettings.small-margin;
    }
    if widget-mode == WidgetMode.edit: Button {
        x: EditorSizeSettings.standard-margin;
        width: 100px;
        text: "Apply";
        changed pressed => {
            if self.pressed {
                root.clear-focus-panel();
            }
        }
        clicked => {
            WindowManager.apply-current-value(Api.as-slint-brush(PickerData.current-brush-kind, PickerData.current-angle, PickerData.current-brush, PickerData.current-gradient-stops));
        }
    }
}
